
class SyncHeap<T extends Comparable<T>> { 
    //Private members
    private BinaryHeap<T> m_heap;  //Dynamic container
    private T m_poisson;
    private int m_Nmax;   //Though the is dynamic we use a "realistic" max
    private javax.swing.JLabel m_label;
    private javax.swing.JLabel m_titleLabel;
    private int m_Nprod;  //current number active producers
    private int m_Ncons;  //current number active consumers
    private boolean m_printToStd;
    //Constructor
    SyncHeap(int maxSize, T poisson, javax.swing.JLabel hLabel, 
            javax.swing.JLabel hTitleLabel, boolean useTree, boolean printToStd) {
        m_Nmax = maxSize;
	m_poisson = poisson;
        m_label = hLabel;
        m_titleLabel = hTitleLabel;
        m_Nprod = 0;
	m_Ncons = 0;   
        if (useTree) {
            m_heap = new BinaryHeapTree();
        } else {
            m_heap = new BinaryHeapArray();
        }
        m_printToStd = printToStd;
    }
    
    //Methods tracking number Producers and Consumers on this heap
    public void addProducer() { m_Nprod++; }
    public void addConsumer() { m_Ncons++; }
    public void withdrawProducer() { m_Nprod--; }
    public void withdrawConsumer() { m_Ncons--; }
    public int nbrProducers() { return m_Nprod; }
    public int nbrConsumers() { return m_Ncons; }
    
    //Return the poission value
    T getPoisson() { return m_poisson; };
    //Checking that the heap is not full according to the "realistic" user defined
    //max number of elements (not needed for the dynamic container though)
    public boolean isFull() { return m_heap.size() >= m_Nmax; }
    
    //Prints the Heap either on the the Swing label or to std out
    public void printHeap(boolean inLabel, T item, boolean produced) { 
	int maxIndex = m_heap.size() - 1;
        if (maxIndex >= 0) {
            double center = 15. * (double)m_heap.depth(maxIndex);
            int depth = 1; //Start at top, where D=1
            int maxIndexInDepth = m_heap.maxIndexInDepth(depth);
            String heapStr = startString(inLabel);
            for (int i = 0; i <= maxIndex ; i++) {
                int nSpace = (int)m_heap.space(center, depth);
                for (int j=0; j<nSpace; j++) heapStr += spaceString(inLabel);
                T elem = m_heap.get(i);    //should never happen, so no need to do exception
                if (elem == null) System.out.println("ERROR, get(i) gives null");
                heapStr += elem.toString(); 
                if (i == maxIndexInDepth) {
                    heapStr += returnString(inLabel);
                    depth++;
                    maxIndexInDepth = m_heap.maxIndexInDepth(depth);
                }
            }
            heapStr += endString(inLabel);
            String titleString;
            if (produced) { titleString = "AFTER PRODUCING \""; 
            } else { titleString =  "AFTER CONSUMING \""; }
            titleString += item+"\" TOTAL NUMBER ITEMS = "+m_heap.size()+" AND THIS IS THE HEAP:";
            if (inLabel) {
                m_titleLabel.setText(titleString);
                m_label.setText(heapStr);
            } else { 
                System.out.println(titleString);
                System.out.print(heapStr); 
            }
        }
    }
    //Removes the top (the current largest element) and returns it
    public synchronized T removeTop() {
	T top = m_poisson; 
	try {
	    while (m_heap.isEmpty()) {
		if (m_printToStd) System.out.println("Heap is empty, so this consumer have to wait a bit... ");
		wait();
            }  
	    //Return right away if there is poisson in heap, don't make it smaller, coz
	    //we want all consumers to get the poisson to stop consuming
	    if (m_heap.getTop().compareTo(m_poisson) == 0) return m_poisson; 
	    //If not poisson, continue like normal 
            top = m_heap.removeTop(); //cannot be null, coz checked that not empty
	} catch (InterruptedException e) {
	    if (m_printToStd) System.out.println("SyncHeap was interrupted while waiting for empty queue to be filled.");
	}
        //Have to print the heap inside this synchronized removeTop() since outside the lock is released
        //and the heap can be modified before we print the heap and could have lost the heap look
        if (m_printToStd) printHeap(false, top, false);      //To the std out
        printHeap(true, top, false);                         //To the Swing label
	notify();    //in case a producer waiting on a full heap
	return top;  //return old top, which was the biggest number in the heap before it was removed
    }
    //Returns the top (current largest element) without modifying the heap
    public synchronized T getTop() {
	try {
	    while (m_heap.isEmpty()) {
		if (m_printToStd) System.out.println("Heap is empty, so this consumer have to wait a bit... ");
		wait();
	    }
	    return m_heap.getTop();  
	} catch (InterruptedException e) {
	    if (m_printToStd) System.out.println("SyncHeap was interrupted while waiting for empty queue to be filled.");
	}
	return m_poisson;  
    }
    //Adds new element to the heap (and makes sure it is still a heap)
    public synchronized void addElement(T val) {
	try {
	    while (isFull() && (m_Ncons > 0)) {
		System.out.println("Heap is full, so this producer have to wait a bit... ");
		wait();
	    }
	    if (!isFull()) { m_heap.addElement(val); }
	} catch (InterruptedException e) {
	    System.out.println("SyncHeap was interrupted while waiting for full queue to release a position.");
	}
        //Have to print the heap inside this synchronized addElement() since outside the lock is released
        //and the heap can be modified before we print the heap and could have lost the heap look
        if (m_printToStd) printHeap(false, val, true);           //To the std out
        printHeap(true, val, true);                              //To the Swing label
        
	notify();
    }
    
    //Some printing methods (either to the Swing label or to std out) 
    private String startString(boolean inLabel) {
        if (inLabel) return "<html>";
        return "";
    }
    private String spaceString(boolean inLabel) {
        if (inLabel) return "&nbsp;";
        return " ";
    }
    private String returnString(boolean inLabel) {
        if (inLabel) return " <br> ";
        return " \n";
    }
    private String endString(boolean inLabel) {
        if (inLabel) return " <br> </html> ";
        return " \n";
    }
}
